package com.xxxx.seckill.controller;


import com.wf.captcha.ArithmeticCaptcha;
import com.xxxx.seckill.config.AccessLimit;
import com.xxxx.seckill.exception.GlobalException;
import com.xxxx.seckill.pojo.SeckillMessage;
import com.xxxx.seckill.pojo.SeckillOrder;
import com.xxxx.seckill.pojo.User;
import com.xxxx.seckill.rabbitmq.MQSender;
import com.xxxx.seckill.service.IGoodsService;
import com.xxxx.seckill.service.IOrderService;
import com.xxxx.seckill.service.ISeckillOrderService;
import com.xxxx.seckill.utils.JsonUtil;
import com.xxxx.seckill.vo.GoodsVo;
import com.xxxx.seckill.vo.RespBean;
import com.xxxx.seckill.vo.RespBeanEnum;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import sun.rmi.runtime.Log;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 秒杀
 *
 * @author: LC
 * @date 2022/3/4 11:34 上午
 * @ClassName: SeKillController
 */
@Slf4j
@Controller
@RequestMapping("/seckill")
//@Api(value = "秒杀", tags = "秒杀")
public class SecKillController implements InitializingBean {
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private IOrderService orderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MQSender mqSender;
    @Autowired
    private RedisScript<Long> redisScript;

    private Map<Long,Boolean> EmptyStockMap = new HashMap<>();

//    @RequestMapping( "/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        if (user == null) {
//            return "login";
//        }
//        model.addAttribute("user", user);
//        GoodsVo goods = goodsService.findGoodsVOByGoodsId(goodsId);
//        //判断库存
//        if (goods.getStockCount()<1){
//            model.addAttribute( "errmsg",RespBeanEnum.EMPTY_STOCK.getMessage());
//            return "seckillFail";
//        }
//        //判断是否重复抢购----mybatis-plus写法
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
//                .eq("user_id", user.getId())
//                .eq("good_id", goodsId));
//
//
//        if (!Optional.ofNullable(seckillOrder).isPresent()){
//            model.addAttribute( "errmsg" ,RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//        Order order = orderService.seckill(user , goods);
//        model.addAttribute( "order", order);
//        model.addAttribute(  "goods", goods);
//        return "orderDetail";
//     }


    /**
     * @Author heyanfeng
     * @Description
     * 功能描述:     验证码生成
     * @Date 15:41 2022/9/1
     * @Param [user, goodsId, response]
     * @return void
     **/
    @RequestMapping(value = "/captcha" ,method = RequestMethod.GET)
    public void verifyCode(User user,Long goodsId, HttpServletResponse response){
        if (user ==null||goodsId<0){
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //设置请求头为输出图片的类型
        response.setContentType("image/jpg");
        response.setHeader("Pargam","No-cache");
        response.setHeader( "Cache-Control", "no-cache");
        response.setDateHeader( "Expires",  0);
        //生成验证码，将结果放入Redis
        ArithmeticCaptcha captcha = new ArithmeticCaptcha( 130, 32, 3);
        redisTemplate.opsForValue().set("captcha:"+user.getId()+":"+goodsId,captcha.text() ,300,TimeUnit.SECONDS);
        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码生成失败",e.getMessage());
        }

    }


    /**
     * @Author heyanfeng
     * @Description
     * 功能描述:    获取秒杀地址
     * @Date 12:35 2022/9/1
     * @Param [user, goodsId]
     * @return com.xxxx.seckill.vo.RespBean
     **/
    @AccessLimit(second = 5, maxCount = 5, needLogin = true )
    @RequestMapping(value = "/path",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request){
        if(user == null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }


//        ValueOperations valueOperations = redisTemplate.opsForValue();
//        //限制访问次数，5秒访问5次 （计数器方式） 限流
//        String uri = request.getRequestURI();
//        captcha = "0";
//        Integer count = (Integer) valueOperations.get(uri + ":" + user.getId());
//        if(count == null){
//         valueOperations.set(uri + ":" + user.getId(),1,5,TimeUnit.SECONDS);
//        }else if(count <5){
//            valueOperations.increment(uri + ":" + user.getId());
//        }else {
//            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
//        }


        boolean check =  orderService.checkCaptcha(user,goodsId,captcha);
        if(!check){
            return  RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }
        String str = orderService.createPath(user,goodsId);
        return RespBean.success(str);
    }

    /**
     * @Author heyanfeng
     * @Description
     * 功能描述:      秒杀功能
     * @Date 18:20 2022/9/2
     * @Param [path, user, goodsId]
     * @return com.xxxx.seckill.vo.RespBean
     **/
    @RequestMapping(value= "/{path}/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSeckill(@PathVariable String path, User user, Long goodsId) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        //校验秒杀地址
        ValueOperations valueOperations = redisTemplate.opsForValue();
       boolean check = orderService.checkPath(user,goodsId,path);
       if(!check){
           return  RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
       }

        //判断是否重复抢购
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder != null) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        //利用内存标记减少Redis的访问
        if (EmptyStockMap.get(goodsId)){
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        //预减库存  redis的分布式锁使用
        //Long stock = valueOperations.decrement("seckillGoods:" + goodsId);
        Long stock = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId), Collections.EMPTY_LIST);
        if (stock < 0) {
            EmptyStockMap.put(goodsId, true);
            //避免了库存显示-1的情况
            valueOperations.increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //下单, 使用RabbitMQ进行下单
        SeckillMessage seckillMessage = new SeckillMessage(user,goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));
        return RespBean.success(0);
//
//       // model.addAttribute("user", user);
//        GoodsVo goods = goodsService.findGoodsVOByGoodsId(goodsId);
//        //判断库存
//        if (goods.getStockCount()<1){
//         //   model.addAttribute( "errmsg",RespBeanEnum.EMPTY_STOCK.getMessage());
//            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
//        }
//
//        /**
//         * @Description
//         * 问题描述：库存超卖问题
//         * 原理: update行级的排他锁，原子性操作，可保证线程安全
//         * 库存超卖，订单库存不统一问题：
//         * 1、扣库存这里利用sql语句处理，且加了库存数量的限制必须大于0
//         * 2、在秒杀订单表中加唯一索引（用户id+商品id的唯一索引，解决同一个用户秒杀多商品的问题）
//         * 3、优化重复抢购直接进Redis处理
//         * 4、redis 预减库存
//         */
//
//        //判断是否重复抢购----mybatis-plus写法
////        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
////                .eq("user_id", user.getId())
////                .eq("good_id", goodsId));
//
//        SeckillOrder seckillOrder = (SeckillOrder)redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
//
//        if (!Optional.ofNullable(seckillOrder).isPresent()){
//           // model.addAttribute( "errmsg" ,RespBeanEnum.REPEATE_ERROR.getMessage());
//            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
//        }
//        Order order = orderService.seckill(user , goods);
////        model.addAttribute( "order", order);
////        model.addAttribute(  "goods", goods);

    }


    /**
     * @Author heyanfeng
     * @Description 
     * 功能描述:      获取秒杀结果
     *  orderId :成功 ，-1：秒杀失败，0：排队中
     * @Date 15:09 2022/8/30
     * @Param [user, goodsId]
     * @return com.xxxx.seckill.vo.RespBean
     **/
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(User user, Long goodsId){
        if(user == null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
       Long orderId =  seckillOrderService.getResult(user,goodsId);
       return RespBean.success(orderId);
    }





    /**
     * @Author heyanfeng
     * @Description
     * 功能描述:    系统初始化，把商品库存数量加载到Redis
     * @Date 11:41 2022/8/30
     * @Param []
     * @return void
     **/
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> list = goodsService.findGoodsVO();
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        list.forEach(goodsVo ->{
            redisTemplate.opsForValue().set("seckillGoods:"+goodsVo.getId(),goodsVo.getStockCount());
            EmptyStockMap.put(goodsVo.getId(),false);
        });

    }
}
